﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.IO;
using System.Reflection;

namespace Kollektiv.Extension
{
    public static class System_Object
    {
        /// <summary>
        /// Serailizes an object into a stream.
        /// </summary>
        /// <param name="obj">Object to be serialized.</param>
        /// <param name="stream">Stream object should be serialized in.</param>
        public static void SerializeDataContract(this object obj, Stream stream)
        {
            if (!obj.GetType().IsClass || !obj.GetType().GetCustomAttributes(true).Where(a => a is DataContractAttribute).Any())
                throw (new ArgumentException("object to serialize is not a class or no data contract"));

            DataContractSerializer serializer = new DataContractSerializer(obj.GetType());

            serializer.WriteObject(stream, obj);
        }

        /// <summary>
        /// Serailizes an object into a string.
        /// </summary>
        /// <param name="obj">Object to be serialized.</param>
        /// <returns>String containing serialized object.</returns>
        public static string SerializeDataContract(this object obj)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                obj.SerializeDataContract(ms);

                byte[] buf = new byte[ms.Length];
                ms.Read(buf, 0, Convert.ToInt32(ms.Length));

                return Encoding.UTF8.GetString(buf);
            }
        }

        /// <summary>
        /// Sets a property value on the object.
        /// </summary>
        /// <param name="obj">Affected object.</param>
        /// <param name="propertyName">Name of the property to set.</param>
        /// <param name="value">Value to set.</param>
        public static void SetPropertyValue(this object obj, string propertyName, object value)
        {
            if (!obj.GetType().IsClass)
                throw (new ArgumentException("object is not a class"));

            PropertyInfo pi = obj.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
            
            if(pi == null)
                throw(new ArgumentException(string.Concat("the object of type <", obj.GetType(), "> contains no public property <", propertyName, ">")));

            pi.SetValue(obj, value, null);
        }

        /// <summary>
        /// Gets a property value of the object.
        /// </summary>
        /// <param name="obj">Affected object.</param>
        /// <param name="propertyName">Name of the property to get.</param>
        /// <returns>Value of the property.</returns>
        public static object GetPropertyValue(this object obj, string propertyName)
        {
            if (!obj.GetType().IsClass)
                throw (new ArgumentException("object is not a class"));

            PropertyInfo pi = obj.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);

            if (pi == null)
                throw (new ArgumentException(string.Concat("the object of type <", obj.GetType(), "> contains no public property <", propertyName, ">")));

            return pi.GetValue(obj, null);
        }
    }
}
